package deanandjames.sound;

import javax.sound.midi.*;
import java.util.HashMap;
import java.util.Map;

public class MidiLoader implements MetaEventListener {
	private static final int END_OF_TRACK = 47;
	private MidiInfo currentMidi = null;
	private final Map<String, MidiInfo> midisMap;
	private Sequencer sequencer;
	private final SoundWatcher watcher = null;

	public MidiLoader() {
		midisMap = new HashMap<String, MidiInfo>();
		initSequencer();
	}

	public void close() {
		stop();
		if (sequencer != null) {
			if (sequencer.isRunning()) {
				sequencer.stop();
			}
			sequencer.removeMetaEventListener(this);
			sequencer.close();
			sequencer = null;
		}
	}

	public void load(String name, String fnm) {
		if (midisMap.containsKey(name)) {
			System.out.println("Error: " + name + "already stored");
		} else if (sequencer == null) {
			System.out.println("No sequencer for: " + name);
		} else {
			midisMap.put(name, new MidiInfo(name, fnm, sequencer));
			System.out.println("-- " + name + "/" + fnm);
		}
	}

	public void meta(MetaMessage meta) {
		if (meta.getType() == END_OF_TRACK) {
			final String name = currentMidi.getName();
			boolean hasLooped = currentMidi.tryLooping();
			if (!hasLooped) {
				currentMidi = null;
			}
			if (watcher != null) {
				if (hasLooped) {
					watcher.atSequenceEnd(name, SoundWatcher.REPLAYED);
				} else {
					watcher.atSequenceEnd(name, SoundWatcher.STOPPED);
				}
			}
		}
	}

	public void play(String name, boolean toLoop) {
		final MidiInfo mi = midisMap.get(name);
		if (mi == null) {
			System.out.println("Error: " + name + "not stored");
		} else {
			if (currentMidi != null) {
				System.out.println("Sorry, " + currentMidi.getName() + " already playing");
			} else {
				currentMidi = mi;
				mi.play(toLoop);
			}
		}
	}

	private void initSequencer() {
		try {
			sequencer = MidiSystem.getSequencer();
			if (sequencer == null) {
				System.out.println("Cannot get a sequencer");
				return;
			}
			sequencer.open();
			sequencer.addMetaEventListener(this);
			if (!(sequencer instanceof Synthesizer)) {
				System.out.println("Linking the MIDI sequencer and synthesizer");
				final Synthesizer synthesizer = MidiSystem.getSynthesizer();
				final Receiver synthReceiver = synthesizer.getReceiver();
				final Transmitter seqTransmitter = sequencer.getTransmitter();
				seqTransmitter.setReceiver(synthReceiver);
			}
		} catch (final MidiUnavailableException e) {
			System.out.println("No sequencer available");
			sequencer = null;
		}
	}

	private void stop() {
		if (currentMidi != null) {
			currentMidi.stop();
		} else {
			System.out.println("No music playing");
		}
	}
}
